---PlayWriter: Castles and Creatures---
A 4am crack                  2021-02-06
---------------------------------------

Name: PlayWriter: Castles and Creatures
Genre: productivity
Year: 1985
Publisher: Woodbury Computer Associates
Platform: Apple ][+ or later
Media: 5.25-inch disk
Sides: 1
OS: DOS 3.3
Previous cracks: none
Similar cracks:
  #2303 PlayWriter: Adventures in Space

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  fails about halfway through

Locksmith Fast Disk Backup
  copies everything except T14,S06, but
  copy loads almost all the way then
  says "PlayWriter Can ONLY Be Run from
  Your Original Master Diskette" and
  hangs

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  "bad" sector seems to exist and
  appears normal

Disk Fixer
  as expected, reading the "bad"
  sector fails, but going to
  INPUT/OUTPUT CONTROL (press "O") and
  setting CHECKSUM ENABLED = NO, the
  sector is readable and appears empty

Why didn't COPYA work?
  intentionally corrupted sector

Why didn't Locksmith FDB work?
  presumably a runtime check to ensure
  the corrupted sector is unreadable

EDD worked. What does that tell us?
  probably just a runtime check on the
  bad sector and not anything crazy
  like half or quarter tracks

Booting the original disk, I hear the
familiar disk grinding / recalibration
of a failed attempt to read a bad
sector, but it's very late in the boot
process -- after the title screen, after
the copyright text screen, while it says
"loading Playfiler". Tracing from the
earliest stage of the boot process is
overkill, so I will tackle it from
another direction.

                   ~

               Chapter 1
  In Which We Flail in All Directions


Firstly, always search for the error
message, in case the protection code is
nearby. I searched for different parts
of the (rather long) protection failure
message, and eventually I found it in
low-ASCII on track $1E:

                 --v--

-------------- DISK EDIT --------------
TRACK $1E/SECTOR $06/VOLUME$FE/BYTE $F8
---------------------------------------
$C8: 65 00 50 6C 61 79 57 72   e.PlayWr
$D0: 69 74 65 72 20 43 61 6E   iter Can
$D8: 20 4F 4E 4C 59 20 42 65    ONLY Be
$E0: 20 52 75 6E 20 66 72 6F    Run fro
$E8: 6D 20 59 6F 75 72 20 4F   m Your O
$F0: 72 69 67 69 6E 61 6C 20   riginal
$F8:>4D<61 73 74 65 72 20 44   Master D

                 --^--

Unfortunately, the surrounding bytes do
not appear to be code. At least, they
are recognizable as neither assembly
language nor BASIC.

Secondly, always check the disk map in
Copy ][+, in case the corrupted sector
is part of a file. (I have seen this on
other disks. It allows you to write the
protection logic entirely in BASIC or
some other high-level language.)

                 --v--

   TRACK           1               2
   0123456789ABCDEF0123456789ABCDEF012

S0    JFEDBBAYXTTRPM AAAAABBBBCDEFGGHI
E1    JFDDBBAYXTTQOM AAAAABBBBCDEFGGHI
C2    JFDDBBZYWTTQOL AAAAABBBBBDDFGGHH
T3    FFDDBBZXWTTQOL AAAAABBBBBDDFGGHH
O4    FFDCBBZXWTSPOL AAAAABBBBBDDFGGHH
R5    FFDCBBZXWTSPNL AAAAABBBBBDDFGGHH
 6    FFDCBBZXVTSPNL AA AABBBBBDDFGGHH
 7    FFDCBBZXVTSPNL AAAAABBBBBDDFGGHH
 8    FFDBBBZXVTSPNK AAAAABBBBBDDFFGHH
 9    FEDBBBZXVTSPNK AAAAABBBBBDDEFGHH
 A    FEDBBAZXVTRPNK AAAAABBBBBDDEFGHH
 B    FEDBBAZXVTRPNK AAAAABBBBBCDEFGHH
 C    FEDBBAZXVTRPNK AAAAABBBBBCDEFGGH
 D    FEDBBAZXVTRPN  AAAAAABBBBCDEFGGH
 E    FEDBBAZXUTRPNG AAAAAABBBBCDEFGGH
 F    FEDBBAZXUTRPMG AAAAAABBBBCDEFGGH

                 --^--

Tracks 0-2 are DOS; track $11 is the
disk catalog. The only sector that is
marked as used but not mapped to a file
is... T14,S06, the unreadable sector.

Combined with the fact that the original
disk audibly grinds like any DOS 3.3
disk would when it tries and fails to
read a sector, this strongly implies
that I am looking for a standard sector
read followed by code that checks that
the read failed successfully.

How do you read a sector under DOS 3.3?
Since there's no file mapped to that
sector, we're probably looking for an
assembly language routine. (It would be
difficult, though not impossible, to set
up the parameters entirely from BASIC.)

  - JSR $BD00, the most "low-level"
    entry point that doesn't involve
    fiddling with softswitches.
    Searching the disk for "20 00 BD"
    finds only the one expected match on
    T00,S01, which is part of DOS.

  - JSR $B7B5, the "higher level" entry
    point. Searching the disk for "20 B5
    B7" finds only the expected matches
    within the DOS area on tracks 0-2.

I am nowhere.

                   ~

               Chapter 2
In Which We Have A Flash of Insight (*)
       And Finally Get Somewhere

(*) Luck


Still nowhere in finding the protection
routine, I have a crazy idea: put my
original disk in slot 6, but boot my
non-working copy from slot 5.

Hear me out. The disk boots DOS 3.3
(seemingly unmodified) and the program
is file-based, which means it should
boot from any slot unless they're going
out of their way to force slot 6. But
the protection routine might assume the
disk is in slot 6. (Many protection
routines do this.)

[S6,D1 = original disk]
[S5,D1 = non-working copy]

]PR#5
...read read read...
...switches to slot 6 briefly...
...grinds...
...protection check passes...
...continues to main menu...

Aha! But how does this help us? I'm not
sure yet. But the bad sector is track
$14, sector $06. Maybe the protection
routine has its own RWTS parameter
table, including a hard-coded reference
to slot 6?

Turning once again to my trusty Disk
Fixer sector editor, a search for the
byte sequence $14 $06 finds several
matches:

                 --v--

------------- DISK SEARCH -------------

$0E/$0F-$05   $0E/$0F-$0E   $1F/$08-$0C

                 --^--

The matches on track $0E are part of
some mostly-textual application data.
But the match on track $1F looks very
familiar:

                 --v--

-------------- DISK EDIT --------------
TRACK $1F/SECTOR $08/VOLUME $FE/BYTE$0C
---------------------------------------
$00: 60 A9 01 D0 F9 00 00 00   `).Py...
$08: 01 60 01 FE>14<06 2F 60   .`.~../`
$10: 87 4F 00 00 01 00 00 60   .O.....`
$18: 01 00 01 EF D8 00 00 00   ...oX...

                 --^--

Thta is definitely an RWTS parameter
table, starting at byte offset $08. As
I thought, it hard-codes the slot and
drive ($60 for slot 6, $01 for drive 1)
as well as the track and sector number
of the intentionally corrupted sector.

But wait, there's more! Immediately
before this parameter table looks like
it might be executable code.

                 --v--

----------- DISASSEMBLY MODE ----------
0000:60             RTS
0001:A9 01          LDA   #$01
0003:D0 F9          BNE   $FFFE

                 --^--

Following the track/sector map, the
previous sector of this file is on
T1F,S09.

                 --v--

----------- DISASSEMBLY MODE ----------
00EA:A9 00          LDA   #$00
00EC:85 09          STA   $09

; address of RWTS parameter table
00EE:A9 60          LDA   #$60
00F0:A0 1E          LDY   #$1E

; execute RWTS command (read)
00F2:20 D9 03       JSR   $03D9

; check RWTS error code
00F5:AD 2B 60       LDA   $602B

; is it "drive error"?
00F8:C9 40          CMP   #$40

; no -> branch to failure path
00FA:D0 05          BNE   $0101

; read failed successfully (original)
00FC:A9 00          LDA   #$00
00FE:85 08          STA   $08
0100:60             RTS

; read unexpectedly succeeded (copy)
0101:A9 01          LDA   #$01
0103:D0 F9          BNE   $00FE

                 --^--

Several things to note here. Firstly, I
forgot that there was an even higher-
level entry point to the DOS 3.3 RWTS,
the vector at $3D9. This contains a JMP
$B7B5, unless you have a really old
program running on a really old Apple II
with less than 48K, in which case the
RWTS entry point will be at a lower
address like $77B5 or even $37B5.

Silly me for forgetting this.

Secondly, and more importantly, I see
that the protection routine is not
directly tied to the error message. If
it fails, the only difference is the
value that ends up in zero page $08 --
#$00 for an original, #$01 for a copy.
To bypass the protection, we just need
to ensure that $08 ends up with #$00.

; change code at offset $C9 to branch
; to success path at offset $D7
T1F,S09,$EE: A9 61 -> F0 0E

The final code looks like this:

                 --v--

----------- DISASSEMBLY MODE ----------
00EA:A9 00          LDA   #$00
00EC:85 09          STA   $09

; always branches
00EE:F0 0E          BEQ   $00FE
...
00FE:85 08          STA   $08
0100:60             RTS

                 --^--

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 2312
------------------EOF------------------
